from game_agent import GameAgent, NO_LIMIT
from loa_game import *
from our_adds.euler_num import *
from our_adds.com import *
from our_adds.mobility import Mobility
from utils import PriorityQueue
from general import *
from time import clock
import random
import copy
#from alpha_beta_improved import AlphaBetaSearchImproved
'''
Created on May 31, 2011

@author: bob
'''
#from alpha_beta_improved import SAFE_EXT
INFINITY = 1.0e400
SAFE_EXT = 1
NO_LIMIT = -1

DEF_MAX_DEPTH = 5
DEF_WIN_SIZE = 0.1
DEF_STR_WEIGHT = 0.5

class DummyAgent(GameAgent):
    def move(self, game_state):
        keys = game_state.getSuccessors().keys()
        return keys[random.randint(0,len(keys)-1)]
    
    def setup(self, player, game_state, turn_time_limit, setup_time_limit):
        return

class AgentSmith(GameAgent):
    '''
    classdocs
    '''
    def __init__(self, windowSize=DEF_WIN_SIZE, maxDepth = DEF_MAX_DEPTH, strategicWeight = DEF_STR_WEIGHT):
        self.qs = None
        self.com = None
        self.mob = None
        self.succ = None
        self.player = None
        self.moves = {}
        self.turn_time_limit = NO_LIMIT
        self.windowSize = windowSize
        self.max_depth = maxDepth
        self.strategicWeight = strategicWeight
        
    def setup(self, player, game_state, turn_time_limit = NO_LIMIT, setup_time_limit = NO_LIMIT):
        ' time issues '
        initTime = clock()
        ' some basic stuff '
        self.turn_time_limit = turn_time_limit - SAFE_EXT
        self.player = player
        safe_setup_time_limit = float(setup_time_limit) - SAFE_EXT
        if (clock() - initTime < safe_setup_time_limit):
            self.qs = QuadState(game_state)
        else:
            return
        if (clock() - initTime < safe_setup_time_limit):
            self.com = Com(game_state)
        else:
            return
        if (clock() - initTime < safe_setup_time_limit):
            self.mob = Mobility(game_state)      
        return
    
    def utility(self, gameState):
        action = self.moves.get(gameState,None)
        self.mob = Mobility(gameState)
        if action is None:
            self.qs = QuadState(gameState)
            self.com = Com(gameState)
#            reinitilize heuristic params
        else:
            self.com.updateComAction(action, gameState)
            self.qs.makeMove(gameState, action)    
        concentration = self.com.concentration(self.player)
        succs,moves = self.getSuccImproved(gameState)
        mobility = self.mob.mobility(succs)
        ccs = self.qs.eulerNumber()
        ccs = ccs[gameState.getCurrentPlayer()]
#        print "#(#^&*#$^&*#^*&$)",concentration, mobility, ccs
        area = self.com.getEnclosingArea(gameState.getCurrentPlayer())
#        print 10*concentration, 5*mobility, 0.6*ccs, 0.8*area
        return ((1-self.strategicWeight)*(10*concentration+0.6*ccs+0.8*area))+(self.strategicWeight*5*mobility)
        
    
    def move(self, gameState):
#        print gameState.getCurrentPlayer()
#        action = self.moves.get(gameState,None)
#        if action is None:
#            self.qs = QuadState(gameState)
#            self.com = Com(gameState)        
#            return
#        else:
#            self.com.updateComAction(action, gameState)
#            self.qs.makeMove(gameState, action)
#        self.mob = Mobility(gameState)
        chosenAction, resultingState = self.search(gameState, self.turn_time_limit)
        return chosenAction
    
    def getSuccImproved(self, gameState):
        current_player = gameState.getCurrentPlayer()
        if current_player == WHITE:
            enemy = BLACK
        else:
            enemy = WHITE
        '''
        long-ass evaluator function for evaluating actions without the resulting board
        '''
        def evaluator(pair):
            action = pair[0]
            initVal = 1.0
            value = initVal
            startRow, startCol = action.row, action.col
            ''' if is MoveAction'''
            if isinstance(action, MoveAction):
                destRow = action.row+action.direction.delta[0]
                destCol = action.col+action.direction.delta[1]
#               reward capturing 
                if gameState.board[destRow][destCol] == enemy:
                    value = value * 2
                squares = getCellSquares(gameState.board, destRow, destCol)
                afterSquares = getCellSquares(gameState.board,
                    destRow, destCol, {(destRow,destCol):current_player})
#                #print afterSquares
                old, new = countQuads(squares, enemy), countQuads(afterSquares, enemy)
    #            bonus breaking enemy quads
                if new[4] - old[4] < 0 or new[3] - old[3]<0:
                    value = value*2
                    #print "break squad"
    #punish for capturing single enemy unit
                            
                if new[0] == 4 and gameState.board[destRow][destCol] == enemy:
                    value = value/ 8   
                    #print "capture single"    
    #moving towards/away from COM
                com = self.com.getCom()
                com = com[current_player]
#               movement towards center of mass 
                oldDist = max(abs(com[COM_ROW]-startRow),abs(com[COM_COL]-startCol))
                newDist = max(abs(com[COM_ROW]-destRow),abs(com[COM_COL]-destCol))
                if oldDist > newDist:
                    value = value * 2
                    #print "closer to com"
                elif oldDist < newDist:
                    value = value / 2
                    #print "further from com"
            
            elif isinstance(action, SpinAction):        
#   check whether this move actualy changes anything:
                square = getSquare(gameState.board, startRow, startCol)
                colorNums = countColors(list(square))
#               the square contains cells of the same player - spin doesnt do anything 
                if colorNums[WHITE] == 4 or colorNums[BLACK] == 4 or colorNums[EMPTY] == 4:
                    return value
#               okay, now we know we have to count:
                beforeSquares = getSquareNeighbourSquares(gameState.board, startRow, startCol)
                vetoDict = {(startRow, startCol):gameState.board[startRow+1][startCol], 
                    (startRow, startCol+1): gameState.board[startRow][startCol],
                    (startRow+1, startCol+1):gameState.board[startRow][startCol+1],
                    (startRow+1, startCol):gameState.board[startRow+1][startCol+1] } 
                afterSquares = getSquareNeighbourSquares(gameState.board, startRow, startCol, vetoDict)    
                old, new = countQuads(beforeSquares, current_player), countQuads(afterSquares, current_player)
#                #print beforeSquares
#                #print old
#                #print new
                if new[4] - old[4] < 0:
                    value = value/2
                    if new[3] - old[3] < 0:
                        value = value / 2
                    #print "break Q4"
                elif new[4] - old[4] > 0:
                    value = value * 2
                    if new[3] - old[3] > 0:
                        value = value * 2
                    #print "gain Q4"
                elif new[3] - old[3] < 0:
                    value = value / 2
                    #print "break Q3"
                elif new[3] - old[3] > 0:
                    value = value * 2
                    #print "gain Q3"
            return (-value)
#           end of evaluator function - continue with the function
                
        successors, moves = PriorityQueue(evaluator), {}
        for row, row_data in enumerate(gameState.board):
            for col, cell in enumerate(row_data):
                if cell == current_player:
                    for direction in DIRECTIONS:
                        ##print "dir: "+str(direction),direction.delta
                        ' function _getMoveAction changed to getActionImproved '
                        action, state = getActionImproved(gameState, row, col, direction)
                        if action is not None:
    #                        successors[action] = state
                            #print action
                            val = evaluator((action,))
                            #print "finalValue="+str(val)
                            successors.append((action,state))
                    for spin in [(row, col), (row - 1, col), (row, col - 1), (row - 1, col - 1)]:
                        action, state = gameState._getSpinAction(spin[0], spin[1])
                        if action is not None:
    #                        successors[action] = state
                            #print action
                            val = evaluator((action,))
                            #print "finalValue="+str(val)
                            successors.append((action, state))
                            moves[state] = action
        return successors, moves    
        
        
        
        
    def search(self, current_state, time2run):
        '''
        Search game to determine best action; use alpha-beta pruning.
        
        @param current_state: The current game state to start the search from.
        '''
        
        ' set start time '
        initTime = clock()
        safe_time2run = float(time2run) - SAFE_EXT
        best_value = -INFINITY
        ''' I think we should find something better here.........'''
        (best_action,bestState) = get1Successor(current_state) 
        if (clock() - initTime < safe_time2run):
            ' ~~ changed here : use priority queue '
            res = self.getSuccImproved(current_state)
            successors = res[0]
        else:
            return best_action,bestState
        
        while ((len(successors) != 0) and (clock() - initTime < safe_time2run)):
            ' remove one item from the queue '
            (action, state) = successors.pop()
            ' for case there is no time to return nothing'
            #for action, state in current_state.getSuccessors().items():
            value_fn = self._getValueFn(current_state)
            value = value_fn(state, best_value, INFINITY, 1, initTime, safe_time2run)
            if value > best_value:
                best_value = value
                best_action = action
                bestState = state
        return best_action,bestState
    
    def _getValueFn(self, state):
        if state.getCurrentPlayer() == self.player:
            return self._maxValue
        else:
            return self._minValue
    
    def _cutoffTest(self, state, depth):
        return depth >= self.max_depth or (state.getWinner() is not None)
    
    def _maxValue(self, state, alpha, beta, depth, initTime, safe_time2run):
#        if self._cutoffTest(state, depth):
#            return self.utility(state)
        winner = state.getWinner()
        if winner == self.player:
            return INFINITY
        elif winner is not None and winner != TIE:
            return -INFINITY
        elif self.max_depth <= depth:
            return self.utility(state)
        'TODO add time here and in the following code'        
        nDeveloped = 0
        value = -INFINITY
        if (clock() - initTime < safe_time2run):
            ' ~~ changed here : use priority queue '
            successors, moves = self.getSuccImproved(state)
#            self.moves = moves
            nActions = self.windowSize*len(successors)
        else:
            return value
        while ((len(successors) != 0) and (clock() - initTime < safe_time2run)
                and nDeveloped <= nActions):
            ' remove one item from the queue '
            (action, successor) = successors.pop()
            #for successor in successors.values():s
            currentCom, currentQs = copy.copy(self.com), copy.copy(self.qs)
            value_fn = self._getValueFn(state)
            value = max(value, value_fn(successor, alpha, beta, depth + 1, initTime, safe_time2run))
            if value >= beta:
#                print nDeveloped
                return value
            alpha = max(alpha, value)
            self.com, self.qs = currentCom,currentQs
            nDeveloped = nDeveloped + 1
        return value
    
    def _minValue(self, state, alpha, beta, depth, initTime, safe_time2run):
        winner = state.getWinner()
        if  winner == self.player:
            return INFINITY
        elif winner is not None and winner != TIE:
            return -INFINITY
        elif self.max_depth <= depth:
            return self.utility(state)
        nDeveloped = 0
        value = INFINITY
        if (clock() - initTime < safe_time2run):
            ' ~~ changed here : use priority queue '
            successors, moves = self.getSuccImproved(state)
#            self.moves = moves
            nActions = self.windowSize*len(successors)
        else:
            return value
        while ((len(successors) != 0) and (clock() - initTime < safe_time2run) 
               and nDeveloped <= nActions):
            ' remove one item from the queue '
            
            action,successor = successors.pop()
            currentCom, currentQs = copy.copy(self.com), copy.copy(self.qs)
            #for successor in successors.values():
            value_fn = self._getValueFn(state)
            value = min(value, value_fn(successor, alpha, beta, depth + 1, initTime, safe_time2run))
            if value <= alpha:
                return value
            beta = min(beta, value)
            self.com, self.qs = currentCom, currentQs
            nDeveloped = nDeveloped + 1
        return value
        
        
        
        
        
        
        
        
        
        
        
        